#ifndef MEMALLOCADAPTER_CPP_
#define MEMALLOCADAPTER_CPP_

#define LOGGING_INFO
#include "logger.h"

#include <stdlib.h>
#include <algorithm>
#include <stdio.h>
#include <errno.h>
#include <pthread.h>

#include "memstorage.hpp"
#include "memstorage.cpp"
#include "memstorage_exceptions.hpp"
#include "filestorage.hpp"
#include "filestorage.cpp"


MemStorage* storage = nullptr;

int ma_init(size_t mem, off_t swap, const char* swap_path) {
	try {
		if (storage) {
			LOG_ERROR("already inited");
			errno = EALREADY;
			return 0;
		}
		
		if (mem > swap) {
			errno = EBADR;
			return 0;
		}
		storage = new MemStorage(mem, swap, swap_path);
		return 1;
		
	} catch(MemAllocException& e) {
		LOG_ERROR("%s\n", e.what());
		LOG_PANIC("!!!");
		return 0;
	} catch(std::bad_alloc& e) {
		LOG_ERROR("%s\n", e.what());
		errno = ENOMEM;
		return 0;
	} catch(...) {
		LOG_PANIC("!!!");
		exit(-1);
	}
}

void ma_deinit() {
	try {
		storage->~MemStorage();
		storage = nullptr;
	}
	catch(std::exception& e) {
		LOG_ERROR("%s\n", e.what());
		LOG_PANIC("!!!");
		exit(-1);
	} catch(...) {
		LOG_PANIC("!!!");
		exit(-1);
	}
	
}
size_t a = 0;
size_t ma_alloc(size_t sz) {
	try {
		a++;
		if (!storage) {
			LOG_ERROR("not inited yet");
			errno = ECANCELED;
			return 0;
		}
		DataChunk* chunk = storage->alloc(sz);
		return reinterpret_cast<size_t>(chunk);
		
	} catch(MemAllocException& e) {
		LOG_ERROR("%s\n", e.what());
		MemAllocException::ErrorCode code = e.GetErrorCode();
		if (code == MemAllocException::E_NOMEM)
			errno = ENOMEM;
		else if (code == MemAllocException::E_2BIG)
			errno = E2BIG;
		else if (code == MemAllocException::E_BADF)
			errno = EBADF;
		else if (code == MemAllocException::E_NOBUFS)
			errno = ENOBUFS;
		else
			LOG_PANIC("!!! : <%d>", code);
		LOG_INFO("a : <%lu>", a);
		exit(-2);
		return 0;
	} catch(std::exception& e) {
		LOG_ERROR("%s\n", e.what());
		LOG_PANIC("!!!");
		exit(-1);
	} catch(...) {
		LOG_PANIC("!!!");
		exit(-1);
	}
}

int ma_free(size_t id) {
	try {
		if (!storage) {
			LOG_ERROR("not inited yet");
			errno = ECANCELED;
			return 0;
		}
		
		if (!storage->haveChunk(reinterpret_cast<DataChunk*>(id))) {
			LOG_ERROR("no such chunk : <%lu> <%p>", id, reinterpret_cast<DataChunk*>(id));
			errno = EINVAL;
			return 0;
		}
		
		reinterpret_cast<DataChunk*>(id)->free();

		return 1;
	} catch(MemAllocException& e) {
		MemAllocException::ErrorCode code = e.GetErrorCode();
		if (code == MemAllocException::E_ACTIVE_MEMORY_FREE)
			errno = EBUSY;
		else if (code == MemAllocException::E_BADF)
			errno = EBADF;
		else if (code == MemAllocException::E_INVAL)
			errno = EINVAL;
		else
			LOG_PANIC("!!!");
		return 0;
	} catch(std::exception& e) {
		LOG_ERROR("%s\n", e.what());
		LOG_PANIC("!!!");
		exit(-1);
	} catch(...) {
		LOG_PANIC("!!!");
		exit(-1);
	}
}

void* ma_get(size_t id) {
	try {
		if (!storage) {
			LOG_ERROR("not inited yet");
			errno = ECANCELED;
			return 0;
		}
		if (!storage->haveChunk(reinterpret_cast<DataChunk*>(id))) {
			LOG_ERROR("no such chunk : <%lu> <%p>", id, reinterpret_cast<DataChunk*>(id));
			errno = EINVAL;
			return 0;
		}
		return reinterpret_cast<DataChunk*>(id)->get();
			
	} catch(MemAllocException& e) {
		LOG_ERROR("Error : %s\n", e.what());
		MemAllocException::ErrorCode code = e.GetErrorCode();
		if (code == MemAllocException::E_NOMEM)
			errno = ENOMEM;
		else if (code == MemAllocException::E_NOBUFS)
			errno = ENOBUFS;
		else if (code == MemAllocException::E_BADF)
			errno = EBADF;
		else if (code == MemAllocException::E_INVAL)
			errno = EINVAL;
		else
			LOG_PANIC("!!!");
		return 0;
	} catch(std::exception& e) {
		LOG_ERROR("%s\n", e.what());
		LOG_PANIC("!!!");
		exit(-1);
	} catch(...) {
		LOG_PANIC("!!!");
		exit(-1);
	}
}

int ma_release(size_t id) {
	try {
		if (!storage) {
			LOG_ERROR("not inited yet");
			errno = ECANCELED;
			return 0;
		}
		if (!storage->haveChunk(reinterpret_cast<DataChunk*>(id))) {
			LOG_ERROR("no such chunk : <%lu> <%p>", id, reinterpret_cast<DataChunk*>(id));
			errno = EINVAL;
			return 0;
		}
		reinterpret_cast<DataChunk*>(id)->release();
		return 1;
		
	} catch(MemAllocException& e) {
		LOG_ERROR("Error : %s\n", e.what());
		MemAllocException::ErrorCode code = e.GetErrorCode();
		if (code == MemAllocException::E_ALREADY_RELEASED)
			errno = EFAULT;
		else if (code == MemAllocException::E_INVAL)
			errno = EINVAL;
		else
			LOG_PANIC("!!!");
		return 0;
	}  catch(std::exception& e) {
		LOG_ERROR("%s\n", e.what());
		LOG_PANIC("!!!");
		exit(-1);
	} catch(...) {
		LOG_PANIC("!!!");
		exit(-1);
	}
}

//#include "tests.hpp"
#include "perf_test.c"

#endif //MEMALLOCADAPTER_CPP_
